
#include <BRender>

#include <BConvexHull>
#include "member_BShape.h"
#include "member_BConvexHull.h"

using namespace BWE;

#define member					(*(member_BConvexHull*)_ptr)
#define member_allocate()		_ptr = new member_BConvexHull(this)
#define member_release()		delete (member_BConvexHull*)_ptr

BConvexHull::BConvexHull()
{
	member_allocate();
}
BConvexHull::BConvexHull(const BVectorArray& points)
{
	member_allocate();
	member.vertices = points;
	this->dirty();
}
BConvexHull::BConvexHull(const BArray<BPlane>& planes)
{
	member_allocate();
	member.planes = planes;
	this->dirty();
}

void BConvexHull::copy(const BShape* other)
{
	BShape::copy(other);
	if (const BConvexHull* convex = dynamic_cast<const BConvexHull*>(other))
	{
		member.planes = convex_member(other)->planes;
		member.vertices = convex_member(other)->vertices;
		member.faces = convex_member(other)->faces;
	}
}
BShape* BConvexHull::clone() const
{
	return new BConvexHull(*this);
}

void BConvexHull::addPlane(const BPlane& plane)
{
	member.planes.append(plane);
	this->dirty();
}
void BConvexHull::removePlane(int index)
{
	if (member.planes.remove(index))
	{
		this->dirty();
	}
}

const BVectorArray& BConvexHull::vertices() const
{
	return member.vertices;
}

int BConvexHull::faceCount() const
{
	return member.faces.size();
}
const BIntArray& BConvexHull::face(int index) const
{
	return member.faces(index);
}

bool BConvexHull::containts(const BVector& p) const
{
	for (int i = 0; i < member.planes.size(); i++)
	{
		const BPlane& plane = member.planes[i];
		if (plane.clip(p))
		{
			return false;
		}
	}
	return true;
}
bool BConvexHull::containts(const BPolygon& polygon) const
{
	for (int i = 0; i < polygon.size(); i++)
	{
		const BVector& vertex = polygon(i);
		if (!containts(vertex))
		{
			return false;
		}
	}
	return true;
}

bool BConvexHull::intersects(const BQuad& quad) const
{
	if (containts(quad.a()))
		return true;
	if (containts(quad.b()))
		return true;
	if (containts(quad.c()))
		return true;
	if (containts(quad.d()))
		return true;

	if (member.intersectsSegment(BLine(quad.a(), quad.b())))
		return true;
	if (member.intersectsSegment(BLine(quad.b(), quad.c())))
		return true;
	if (member.intersectsSegment(BLine(quad.c(), quad.d())))
		return true;
	if (member.intersectsSegment(BLine(quad.d(), quad.a())))
		return true;

	return false;
}
bool BConvexHull::intersects(const BSpace& space) const
{
	const BVector& max = space.max();
	const BVector& min = space.min();
	for (int i = 0; i < 6; ++i)
	{
		const BPlane& p = member.planes[i];
		const BVector& n = p.normal();
		BVector extreme(
			(n.x() > 0) ? max.x() : min.x(),
			(n.y() > 0) ? max.y() : min.y(),
			(n.z() > 0) ? max.z() : min.z());
		BReal distance = p.distance(extreme);
		if (distance < 0)
			return false;
	}
	return true;
}
bool BConvexHull::intersects(const BPolygon& polygon) const
{
	for (int i = 0; i < polygon.size(); i++)
	{
		const BVector& vertex = polygon(i);
		if (containts(vertex))
		{
			return true;
		}
	}

	for (int i = 0; i < polygon.size(); ++i)
	{
		const BLine& line = polygon.edgeLine(i);
		if (member.intersectsSegment(line))
		{
			return true;
		}
	}

	return false;
}

void BConvexHull::clear()
{
	member.planes.clear();
	member.vertices.clear();
	member.faces.clear();
	this->dirty();
}

BVector BConvexHull::vertex(const BVector& direct) const
{
	BReal maxDot = -REAL_LARGE;
	BVector vertex;
	if (member.vertices.size())
	{
		int index = MaxDot(direct, member.vertices.data(), member.vertices.size(), maxDot);
		vertex = member.vertices[index];
	}
	return vertex;
}

void BConvexHull::refresh(BBox& box)
{
	member.rebuild();
	box = member.vertices.first();
	BSpace space;
	space = member.vertices.first();
	for (int i = 1; i < member.vertices.size(); i++)
	{
		box.expand(member.vertices[i]);
	}
}
void BConvexHull::render(BRender& render)
{
	for (int i = 0; i < member.faces.size(); i++)
	{
		const BIntArray& face = member.faces[i];
		render.drawPolygon(member.vertices, face);
	}
}

